package test.juc.deadlock;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 让线程1 使用轮询请求锁（tryLock + while循环）的方式，解决死锁 ----- 优化版本1 ----- 解决可能出现的死循环
 *
 * 可能出现死循环的原因：如果线程2一直不释放锁B，线程1的轮询程序就会一直循环
 *
 * @author zhangrui
 * @date 2022-04-18 17:30
 * @since 1.0
 */
public class SolveDeadLockByPollingAdvance1 {
    public static void main(String[] args) {
        Lock lockA = new ReentrantLock(); // 创建锁 A
        Lock lockB = new ReentrantLock(); // 创建锁 B
        // 创建线程 1(使用轮询锁)
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                // 调用轮询锁
                pollingLock(lockA, lockB, 3);
            }
        });
        t1.start(); // 运行线程
        // 创建线程 2
        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                lockB.lock();
                // 加锁
                System.out.println("线程 2:获取到锁 B!");
                try {
                    Thread.sleep(1000);
                    System.out.println("线程 2:等待获取 A...");
                    lockA.lock(); // 加锁
                    try {
                        System.out.println("线程 2:获取到锁 A!");
                    } finally {
                        lockA.unlock(); // 释放锁
                        System.out.println("线程 2:释放锁 A.");
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    /**
                     *
                     * 可能出现死循环的原因：如果线程2一直不释放锁B，线程1的轮询程序就会一直循环
                     *
                     */
                    //lockB.unlock(); // 释放锁
                }
            }
        });
        t2.start(); // 运行线程
    }

    /**
     * 轮询锁
     * 优化点：maxCount：最大轮询次数
     * */
    public static void pollingLock(Lock lockA, Lock lockB, int maxCount) {
        /***
         * 优化点：轮询次数计数器
         */
        int count = 0;
        while (true) {
            if (lockA.tryLock()) {
                // 尝试获取锁
                System.out.println("线程 1:获取到锁 A!");
                try {
                    Thread.sleep(100); // 等待 0.1s(获取锁需要的时间)
                    System.out.println("线程 1:等待获取 B...");
                    if (lockB.tryLock()) {
                        // 尝试获取锁
                        try {
                            System.out.println("线程 1:获取到锁 B!");
                        } finally {
                            lockB.unlock(); // 释放锁
                            System.out.println("线程 1:释放锁 B.");
                            break;
                        }
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    lockA.unlock();
                    // 释放锁
                    System.out.println("线程 1:释放锁 A.");
                }
            }
            /***
             * 优化点：判断是否已经超过最大次数限制
             */
            if (count++ > maxCount) {
                // 终止循环
                System.out.println("轮询锁获取失败,记录日志或执行其他失败策略");
                return;
            }

            System.out.println("线程 1:获取锁 A 失败，1秒后重新尝试.");
            // 等待一秒再继续执行
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
